Skip to content

Support --learn for inline expectation tests - #22228

Open
d10c wants to merge 1 commit into
github:mainfrom
d10c:d10c/learn-inline-expectations-lib
Open

Support --learn for inline expectation tests#22228
d10c wants to merge 1 commit into
github:mainfrom
d10c:d10c/learn-inline-expectations-lib

Conversation

@d10c

@d10c d10c commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Teaches the shared inline-expectations test library to drive codeql test run --learn for inline // $ Alert expectations: when a postprocess test query is run under --learn, the library now emits a learnEdits relation describing how to rewrite the annotated source so the comments match the actual query results.

Requires the matching CLI support for consuming learnEdits; the library change is incompatible CLIs older than that. The CI on this PR should pass only once a supported CLI gets released to nightly.

What it does

For each result/expectation mismatch on the standard // $ tag1 tag2=value tag3[query] // rest of comment…-style tags, learnEdits records a structured edit (append a new comment, remove or rewrite an existing one). The CLI applies these edits to the source file, so --learn updates the inline expectations directly rather than writing a failure grid into .expected. Without --learn the relation is invisible and behaviour is unchanged.

API

  • TestPostProcessing::InputSig gains getStartCommentMarker(...) (and a defaulted getEndCommentMarker), keyed on the source file of a location so a single database can mix languages with different comment syntax. Each per-language utils/test/InlineExpectationsTestQuery.ql supplies its marker (//, #, ...); block-comment languages are left for a follow-up but the end-marker hook is already in place.

Follow-ups not implemented in this PR

  • Block-comment languages (XML/YAML/HTML/ERB/Razor …)
  • Multi-query reconciliation (// $ Alert[query1] Alert[query2])
  • Splitting the tag1,tag2=value shorthand where only one tag needs to be updated.
  • Support for non-qlref/non-postprocessor inline expectations tests.

* The `--learn` postprocessing needs to see *every* parsed expectation on a comment - including
* ones the running test ignores - so it can preserve them when rewriting.
*/
predicate getAnExpectation(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a move of an existing predicate; thought it would be better to leave it as-is. But I can rename it if needed.

@d10c
d10c force-pushed the d10c/learn-inline-expectations-lib branch 2 times, most recently from 92e2729 to 63edc17 Compare July 28, 2026 15:10
`codeql test run --learn` could previously only rewrite `.expected`
files; inline expectations (the `// $ Alert` comments checked by
`InlineExpectationsTest`) had to be fixed by hand. This teaches the
shared test library to compute those source edits so the test runner
can apply them.

The `test-postprocess` query now exposes a `learnEdits` relation
(`file, line, operation, startColumn, endColumn, text`) describing the
minimal source rewrite that would make the inline expectations match the
actual query results. It covers:

  - appending a fresh comment carrying every tag learned for a line that
    has an unexpected result and no existing comment to merge into;
  - rewriting an existing expectation comment as a whole so it matches
    the current results: dropping fixed-spurious tags, promoting a
    `MISSING:` expectation that now fires, clearing a stale `SPURIOUS:`
    annotation, and merging in freshly learned tags, re-rendering the
    remaining expectations (or deleting the comment when none remain);
  - preserving expectations this test does not own -- e.g. a tag
    annotated with a different query's id that shares the source file --
    and any trailing regular note (`// $ Alert // note`);
  - recording any unexpected result, not just `Alert`.

Edits are emitted as a query predicate rather than applied here: the
engine consumes `learnEdits` only under `--learn` and ignores it
otherwise, so ordinary `test run` output is unchanged.

Comment syntax is provided per language by each
`InlineExpectationsTestQuery.ql`'s `Input` (`getStartCommentMarker`),
which for now renders only line-comment languages; block-comment
languages (XML/YAML) can supply their markers later without changing
this relation's shape.
@d10c
d10c force-pushed the d10c/learn-inline-expectations-lib branch from 63edc17 to 52f2ac6 Compare July 28, 2026 16:03
@d10c
d10c requested a review from cklin July 28, 2026 16:17
@d10c
d10c marked this pull request as ready for review July 28, 2026 16:17
Copilot AI review requested due to automatic review settings July 28, 2026 16:17
@d10c
d10c requested review from a team as code owners July 28, 2026 16:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds inline --learn support so test expectations can be rewritten directly in source files.

Changes:

  • Adds structured learnEdits generation and expectation-preserving rewrite logic.
  • Introduces per-file comment-marker APIs.
  • Configures supported source extensions for each language.
Show a summary per file
File Description
shared/util/codeql/util/test/InlineExpectationsTest.qll Implements parsing and learned edits.
cpp/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables C/C++ line comments.
csharp/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables C# line comments.
go/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Go line comments.
java/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Java/Kotlin line comments.
javascript/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables JavaScript/TypeScript line comments.
python/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Python line comments.
ql/ql/src/utils/test/InlineExpectationsTestQuery.ql Enables QL/dbscheme line comments.
ruby/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Ruby line comments.
rust/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Rust line comments.
swift/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables Swift line comments.
unified/ql/lib/utils/test/InlineExpectationsTestQuery.ql Enables unified Swift line comments.

Review details

  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment on lines +675 to +676
* inline expectations use line comments; block-comment languages can override it so that
* `--learn` renders a closing marker.
@d10c d10c added the no-change-note-required This PR does not need a change note label Jul 28, 2026
* `--learn` renders a closing marker.
*/
bindingset[relativePath]
default string getEndCommentMarker(string relativePath) { result = "" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more natural to use none() here as the default result for comments without an end marker. Otherwise, this is likely to break as soon as, say, html comment markers are added to a language that already has // comments for other files, since it's natural to leave out the result = "" disjunct in those other cases.

Comment on lines +1079 to +1084
endMarker = Input2::getEndCommentMarker(relativePath) and
(
endMarker = "" and endSuffix = ""
or
endMarker != "" and endSuffix = " " + endMarker
) and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
endMarker = Input2::getEndCommentMarker(relativePath) and
(
endMarker = "" and endSuffix = ""
or
endMarker != "" and endSuffix = " " + endMarker
) and
(
endSuffix = " " + Input2::getEndCommentMarker(relativePath)
or
endSuffix = "" and not exists(Input2::getEndCommentMarker(relativePath))
) and

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C# C++ Go Java JS no-change-note-required This PR does not need a change note Python QL-for-QL Ruby Rust Pull requests that update Rust code Swift

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants